class A: x=0 y=0def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
id(A)
38051088
A.x, A.y
(0, 0)
- 시점1
a= A()b= A()
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 0], [0, 0], [0, 0])
- 시점2
a.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 1], [1, 1], [0, 1])
- 시점3
b.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 2], [1, 2], [1, 2])
- 시점4
b.f()
현재 인스턴스에서 f가 2번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 3번 실행
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 3], [1, 3], [2, 3])
- 시점5
a.f()
현재 인스턴스에서 f가 2번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 4번 실행
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 4], [2, 4], [2, 4])
- 시점6
c=A()
[A.x, A.y], [a.x, a.y], [b.x, b.y], [c.x, c.y]
([0, 4], [2, 4], [2, 4], [0, 4])
- 시점7
c.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 5번 실행
[A.x, A.y], [a.x, a.y], [b.x, b.y], [c.x, c.y]
([0, 5], [2, 5], [2, 5], [1, 5])
- 신기한점: 각 인스턴스에서 인스턴스이름.f()를 실행한 횟수를 서로 공유하는 듯 하다. (A가 관리하는 것처럼 느껴진다.)
- x와 y는 약간 느낌이 다르다. x는 지점소속, y는 본사소속의 느낌?
이 예제에서 x는 인스턴스오브젝트에 소속된 변수, y는 클래스 오브젝트에 소속된 변수처럼 느껴짐
(약속) 앞으로는 인스턴스 오브젝트에 소속된 변수를 인스턴스 변수라고 하고, 클래스 오브젝트에 소속된 변수를 클래스 변수라고 하자.
- 인스턴스 변수와 클래스 변수를 구분하는 방법? 인스턴스이름.__dict__를 쓰면 인스턴스 변수만 출력된다.
따라서 a. + tab을 눌러서 나오는 변수중 a.__dict__에 출력되지 않으면 클래스 변수이다.
a.__dict__
{'x': 2}
b.__dict__
{'x': 2}
c.__dict__
{'x': 1}
- 이 예제에서 아래는 모두 클래스 변수이다.
a.y, b.y, c.y
(5, 5, 5)
예제2: 인스턴스에서 인스턴스 변수 x 변경 (변경 가능)
- 시점0
class A: x=0 y=0def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
a=A()
[A.x, A.y], [a.x, a.y]
([0, 0], [0, 0])
- 시점1
a.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행
a.f()
현재 인스턴스에서 f가 2번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행
a.f()
현재 인스턴스에서 f가 3번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 3번 실행
[A.x,A.y], [a.x, a.y]
([0, 3], [3, 3])
- 시점2
a.x =0# f의 실행기록을 초기화 하고 싶다.
a.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 4번 실행
[A.x,A.y], [a.x, a.y]
([0, 4], [1, 4])
예제3: 클래스에서 클래스 변수 y 변경 (변경가능)
class A: x=0 y=0def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
a=A()
b=A()
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 0], [0, 0], [0, 0])
- 시점1
a.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 1], [1, 1], [0, 1])
- 시점2
A.y =100
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([0, 100], [1, 100], [0, 100])
a.f()
현재 인스턴스에서 f가 2번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 101번 실행
예제4: 클래스에서 클래스 변수 x 변경 (변경가능)
class A: x=0 y=0def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
a=A()
[A.x, A.y], [a.x, a.y]
([0, 0], [0, 0])
- 시점1
a.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행
[A.x, A.y], [a.x, a.y]
([0, 1], [1, 1])
- 시점2
A.x =100# 이렇게 되면 앞으로 만들어진 인스턴스튼 기본적으로 현재 인스턴스에서| 100번 f를 실행하였다는 정보를 가지고 태어나게 된다.
[A.x, A.y], [a.x, a.y]
([100, 1], [1, 1])
- 시점3
b=A()
[A.x, A.y], [a.x, a.y], [b.x, b.y]
([100, 1], [1, 1], [100, 1])
- 시점4
b.f()
현재 인스턴스에서 f가 101번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행
- 시점5
a.f()
현재 인스턴스에서 f가 2번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 3번 실행
a.f()
현재 인스턴스에서 f가 3번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 4번 실행
b.f()
현재 인스턴스에서 f가 102번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 5번 실행
예제4의 변형
class B: x=100# 초기자본금 y=0def f(self): # f를 실행할때마다 돈을 쓴다.self.x=self.x -1 B.y = B.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 {}원 잔액남음".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 총 {}원 사용".format(self.y))
a=B()
b=B()
[B.x, B.y], [a.x, a.y], [b.x, b.y]
([100, 0], [100, 0], [100, 0])
- 시점1
a.f() # 돈을 쓴다
현재 인스턴스에서 99원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 1원 사용
a.f()
현재 인스턴스에서 98원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 2원 사용
b.f()
현재 인스턴스에서 99원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 3원 사용
- 시점2
[B.x, B.y], [a.x, a.y], [b.x, b.y]
([100, 3], [98, 3], [99, 3])
B.x=999
[B.x, B.y], [a.x, a.y], [b.x, b.y]
([999, 3], [98, 3], [99, 3])
- 시점3
c=B()
c.f()
현재 인스턴스에서 998원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 4원 사용
- 시점4
a.f()
현재 인스턴스에서 97원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 5원 사용
b.f()
현재 인스턴스에서 98원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 6원 사용
c.f()
현재 인스턴스에서 997원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 7원 사용
c.f()
현재 인스턴스에서 996원 잔액남음
A클래스에서 만들어진 모든 인스턴스들에서 총 8원 사용
예제5 : 인스턴스에서 클래스변수 변경 (변경가능???)
class A: x=0 y=0def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
[A.x, A.y]
[0, 0]
a=A()b=A()
[A.x, A.y], [a.x, a.y], [b.x,b.y]
([0, 0], [0, 0], [0, 0])
- 시점1
a.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행
b.f()
현재 인스턴스에서 f가 1번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행
a.f()
현재 인스턴스에서 f가 2번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 3번 실행
- 시점2
a.__dict__
{'x': 2}
a.y # 인스턴스 a에 소속되어 있지만 클래스 변수
3
a.y =999# A.y 였으면 다 바꼈을 테지만 a.y 였다면??# 내가 하드코딩으로 a.y에 999 입력 -> 이것이 A.y나 b.y에도 반영될까? (x)
[A.x, A.y], [a.x, a.y], [b.x,b.y]
([0, 3], [2, 999], [1, 3])
a.__dict__
{'x': 4, 'y': 999}
- 시점3
b.f()
현재 인스턴스에서 f가 2번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 4번 실행
a.f()
현재 인스턴스에서 f가 3번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 999번 실행
b.f()
현재 인스턴스에서 f가 3번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 6번 실행
b.f()
현재 인스턴스에서 f가 4번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 7번 실행
a.f()
현재 인스턴스에서 f가 4번 실행
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 999번 실행
- 요약 - 인스턴스에서 클래스 변수의 값을 변경하면? -> 클래스변수의 값이 변경되는 것이 아니라 인스턴스 변수가 새롭게 만들어져서 할당 된다. - 이 예제에서 a.y는 이제 클래스변수에서 인스턴스 변수로 재탄생 되었다. 즉, 999오브젝트가 새롭게 만들어져서 a.x라는 이름을 얻은것이다. - 기존의 A.y나 b.y에는 아무런 변화가 없다.
id(999) #999도 오브젝트임
139914476320048
a.y = 999 는 새로운 인스턴스 변수 y를 할당하는 역할을 한다. 클래스변수의 값을 변경하는 것이 아니다. (왜냐하면 애초에 a.y는 없는 값이었고, A.y를 빌리고 있었던 것임)
a.__dict__
{'x': 4, 'y': 999}
b.__dict__
{'x': 4}
예제5의 변형
class A: x=0 y=0def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행 (인스턴스레벨)".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (클레스레벨)".format(A.y))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (인스턴스레벨)".format(self.y))
a=A()
a.f()
현재 인스턴스에서 f가 1번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행 (클레스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행 (인스턴스레벨)
b=A()
b.f()
현재 인스턴스에서 f가 1번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행 (클레스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행 (인스턴스레벨)
- 시점1
a.y =999
a.f()
현재 인스턴스에서 f가 2번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 3번 실행 (클레스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 999번 실행 (인스턴스레벨)
a.f()
현재 인스턴스에서 f가 3번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 4번 실행 (클레스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 999번 실행 (인스턴스레벨)
예제6: 인스턴스 생성시점에 대한 분석
- 의문: 아래의 코드에서 x는 클래스 변수라고 봐야할까? 인스턴스 변수라고 봐야할까? —> 클래스 변수!
class SoWhaTV: x=0# 이 시점에서 x는 클래스변수인가? 아니면 인스턴스 변수인가?def f(self):print(self.x)
- 시점0
class A: x=0 y=0def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행 (인스턴스레벨)".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (클레스레벨)".format(A.y))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (인스턴스레벨)".format(self.y))
a=A()b=A()
a.x, a.y, b.x, b.y
(0, 0, 0, 0)
a.__dict__, b.__dict__
({}, {})
지금 시점에서 a.x, a.y, b.x, b.y는 모두 클래스 변수임
- 시점1
a.f()
현재 인스턴스에서 f가 1번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행 (클레스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행 (인스턴스레벨)
a.__dict__, b.__dict__
({'x': 1}, {})
이 순간 a.x가 클래스변수에서 인스턴스 변수로 변경되었다. (예제5와 같이..) 왜? f가 실행되면서 self.x = self.x + 1이 실행되었으므로!
- 시점2
b.f()
현재 인스턴스에서 f가 1번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행 (클레스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행 (인스턴스레벨)
a.__dict__, b.__dict__
({'x': 1}, {'x': 1})
예제7: 잘못된 사용
- 아래처럼 코드를 바꾸면 어떻게 되는가?
class A:def__init__(self):self.x=0# 인스턴스 변수로 나중에 쓸꺼니까 명시함 A.y=0# 클래스변수로 나중에 쓸꺼니까 명시함def f(self):self.x=self.x +1 A.y = A.y +1# self.y = self.y + 1 이렇게 안쓰고 위에처럼 써보자!print("현재 인스턴스에서 f가 {}번 실행 (인스턴스레벨)".format(self.x))print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (클레스레벨)".format(A.y))#print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (인스턴스레벨)".format(self.y))
- 사용
a=A()b=A()
a.f()
현재 인스턴스에서 f가 1번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행 (클레스레벨)
b.f()
현재 인스턴스에서 f가 1번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 2번 실행 (클레스레벨)
b.f()
현재 인스턴스에서 f가 2번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 3번 실행 (클레스레벨)
b.f()
현재 인스턴스에서 f가 3번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 4번 실행 (클레스레벨)
a.f()
현재 인스턴스에서 f가 2번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 5번 실행 (클레스레벨)
- 잘 되는 것 같다?
- 조금만 생각해보면 엉터리라는 것을 알 수 있다. 아래를 관찰하자.
c=A() # 이 시점에서 __init__()이 실행된다
a.f()
현재 인스턴스에서 f가 3번 실행 (인스턴스레벨)
A클래스에서 만들어진 모든 인스턴스들에서 f가 총 1번 실행 (클레스레벨)
클래스 레벨의 변수가 왜 초기화가 되었지?
- 오류의 이유? c=A()가 실행되는 시점에 __init__()이 실행되면서 A.y=0이 실행된다. 따라서 강제 초기화가 진행되었다.